﻿/* ######################################## *\
 * ### Copyright (C) 2009 AJ Ravindiran ### *
\* ######################################## */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using AJRavindiran.Jolt.RuneScape.Models.Characters.Information;

namespace AJRavindiran.Jolt.RuneScape.Models.Characters.Update
{
    /// <summary>
    /// Represents a player movement.
    /// </summary>
    public class Movement
    {
        #region Fields
        private static readonly int SIZE = 50;

        private Character entity;
        private int walkQueueRead = 0, walkQueueWrite = 0;
        private bool isRunning = false, isRunToggled = false;
        private MovementPoint[] walkingQueue = new MovementPoint[SIZE];
        #endregion Fields

        #region Properties
        /// <summary>
        /// Gets or sets whether the character is running.
        /// </summary>
        public bool IsRunning
        {
            get { return this.isRunning; }
            set { this.isRunning = value; }
        }

        /// <summary>
        /// Gets or sets whether if the character has running toggled.
        /// </summary>
        public bool IsRunToggled
        {
            get { return this.isRunToggled; }
            set { this.IsRunToggled = value; }
        }
        #endregion Properties

        #region Constructors
        /// <summary>
        /// Constructs a new movement class.
        /// </summary>
        public Movement(Character entity)
        {
            this.entity = entity;
            for (int i = 0; i < SIZE; i++)
            {
                this.walkingQueue[i] = new MovementPoint();
                this.walkingQueue[i].X = 0;
                this.walkingQueue[i].Y = 0;
                this.walkingQueue[i].Direction = -1;
            }
        }
        #endregion Constructors

        #region Methods
        /// <summary>
        /// Stops the player at the next availible coordinate (square).
        /// </summary>
        public void ResetQueue()
        {
            this.walkingQueue[0].X = this.entity.GetLocation().GetLocalX();
            this.walkingQueue[0].Y = this.entity.GetLocation().GetLocalY();
            this.walkingQueue[0].Direction = -1;
            this.walkQueueRead = this.walkQueueWrite = 0;
        }

        /// <summary>
        /// Adds two coordinates to the walking queue.
        /// </summary>
        /// <param name="x">The x coordinate to add.</param>
        /// <param name="y">The y coordinate to add.</param>
        public void AddToWalkingQueue(int x, int y)
        {
            int diffX = x - this.walkingQueue[walkQueueWrite - 1].X;
            int diffY = y - this.walkingQueue[walkQueueWrite - 1].Y;
            int max = Math.Max(Math.Abs(diffX), Math.Abs(diffY));

            for (int i = 0; i < max; i++)
            {
                if (diffX < 0)
                    diffX++;
                else if (diffX > 0)
                    diffX--;
                if (diffY < 0)
                    diffY++;
                else if (diffY > 0)
                    diffY--;
                AddStepToWalkingQueue(x - diffX, y - diffY);
            }
        }

        /// <summary>
        /// Calculates two coordinates for the walking queue.
        /// </summary>
        public void AddStepToWalkingQueue(int x, int y)
        {
            int diffX = x - this.walkingQueue[walkQueueWrite - 1].X;
            int diffY = y - this.walkingQueue[walkQueueWrite - 1].Y;
            int direction = Utilities.DirectionUtilties.GetDirection(diffX, diffY);

            if (this.walkQueueWrite >= SIZE)
            {
                return;
            }
            if (direction != -1)
            {
                this.walkingQueue[walkQueueWrite].X = x;
                this.walkingQueue[walkQueueWrite].Y = y;
                this.walkingQueue[walkQueueWrite++].Direction = direction;
            }
        }

        public void GetNextMovement()
        {
            entity.GetSprites().ResetSrites();

            if (entity.GetTeleportTo() != null)
            {
                ResetQueue();
                Location lastRegion = entity.GetLocation();
                int regionX = lastRegion.GetRegionX();
                int regionY = lastRegion.GetRegionY();
                entity.SetLocation(entity.GetTeleportTo());
                entity.ResetTeleportTo();
                entity.GetUpdateFlags().DidTeleport = true;

                if ((regionX - entity.GetLocation().GetRegionX()) >= 4 || (regionX - entity.GetLocation().GetRegionX()) <= -4)
                    entity.GetUpdateFlags().DidMapRegionChange = true;
                if ((regionY - entity.GetLocation().GetRegionY()) >= 4 || (regionY - entity.GetLocation().GetRegionY()) <= -4)
                    entity.GetUpdateFlags().DidMapRegionChange = true;
            }
            else
            {
                Location oldLocation = entity.GetLocation();
                int walkDir = this.GetNextWalkingDirection();
                int runDir = -1;

                if (isRunning || isRunToggled)
                {
                    if (entity.GetStatus().RunEnergy > 0)
                    {
                        runDir = this.GetNextWalkingDirection();
                        if (runDir != -1)
                            entity.GetStatus().RunEnergy--;
                    }
                    else
                    {
                        if (isRunToggled)
                        {
                            isRunToggled = false;
                            RuneScape.GetPacketManager().GetFrames().SendPrimaryConfig(entity, 173, 0);
                            isRunning = false;
                        }
                        isRunning = false;
                    }
                }

                Location lastRegion = entity.GetUpdateFlags().LastRegion;
                if (lastRegion != null)
                {
                    int regionX = lastRegion.GetRegionX();
                    int regionY = lastRegion.GetRegionY();

                    if ((regionX - entity.GetLocation().GetRegionX()) >= 4)
                        entity.GetUpdateFlags().DidMapRegionChange = true;
                    else if ((regionX - entity.GetLocation().GetRegionX()) <= -4)
                        entity.GetUpdateFlags().DidMapRegionChange = true;
                    if ((regionY - entity.GetLocation().GetRegionY()) >= 4)
                        entity.GetUpdateFlags().DidMapRegionChange = true;
                    else if ((regionY - entity.GetLocation().GetRegionY()) <= -4)
                        entity.GetUpdateFlags().DidMapRegionChange = true;
                }
                else
                {
                    entity.GetUpdateFlags().DidMapRegionChange = true;
                }

                if (entity.GetUpdateFlags().DidMapRegionChange)
                {
                    if (walkDir != -1)
                        walkQueueRead--;
                    if (runDir != -1)
                        walkQueueRead--;

                    walkDir = -1;
                    runDir = -1;
                    entity.SetLocation(oldLocation);
                }
                entity.GetSprites().SetSprites(walkDir, runDir);
            }
        }

        /// <summary>
        /// Returns the next walking direction.
        /// </summary>
        public int GetNextWalkingDirection()
        {
            if (walkQueueRead == walkQueueWrite) return -1;

            int direction = walkingQueue[walkQueueRead++].Direction;
            int xDifference = Utilities.DirectionUtilties.DIRECTION_DELTA_X[direction];
            int yDifference = Utilities.DirectionUtilties.DIRECTION_DELTA_Y[direction];
            entity.SetLocation(Location.New(entity.GetLocation().X + xDifference, entity.GetLocation().Y + yDifference, entity.GetLocation().Z));
            return direction;
        }
        #endregion Methods
    }
}
